<?php

require_once PATH_FFWD.'core/baseobject.class.php';
require_once PATH_FFWD.'prototypes/table.base.php';
require_once PATH_FFWD.'prototypes/record.base.php';

class Database extends BaseObject{
	private $engine=0;
	private $queries=Array();	
	
	public function escape_string($s){
		return mysqli_real_escape_string($this->engine,$s);
	}
	
	public function __construct($cs=0){
		if(!$cs)throw new Exception('connectionstring not defined');
		$d=Array();
		if(!preg_match('/(.*):(.*)@(.*)\/(.*)/',$cs,$d)){
			throw new Exception("wrong connectionstring format");
			return 0;		
		};
		$this->user=$d[1];
		$this->password=$d[2];
		$this->host=$d[3];
		$this->database=$d[4];
		
		$this->engine=new Mysqli($this->host,$this->user,$this->password,$this->database);
		$this->engine->set_charset('utf8');		
		$this->engine->query('SET names utf8');
		
		$GLOBALS[$this->database]=&$this;
		
	}
	
	public function print_cli(MySQLi_Result &$rs){
		$headerprinted=0;
		$rows=Array();
		$rows[]=array_keys($rs->fetch_assoc());
		while($row=$rs->fetch_assoc()){
			$nr=Array();
			foreach($row as $v)$nr[]=$v;
			$rows[]=$nr;
		};
		print_cli_2d($rows);
	}
	
	public function print_html(MySQLi_Result &$rs){
		if($rs->num_rows){				
			while($row=$rs->fetch_assoc()){
				if(!isset($keys)){
					$keys=array_keys($row);
					echo "<table border='1' cellspacing='0' ><thead><tr>";
					foreach($keys as $key)echo "<td><b>$key</b></td>";
					echo "</tr></thead><tbody>";
				};
				echo "<tr>";
				foreach( $keys as $key )echo "<td>".$row[$key]."</td>";
				echo "</tr>";
			};
			echo "</tbody></table>";
			$rs->data_seek(0);	
		};	
	}
	
	public function insert_id(){
		return mysqli_insert_id($this->engine);
	}
	
	public function print_json(MySQLi_Result &$rs,$options=''){
		echo '[';$c=$rs->num_rows-1;$i=0;
		if($c+1){
			while(true){
				$row=$rs->fetch_assoc();/*
				foreach($row as $k=>$v)
					$row[$k]=str_replace(
						Array('&otilde;','&Otilde;','&ucirc;','&Ucirc;'),
						Array('&#337;','&#336;','&#369;','&#368;'),
						htmlentities($v)
					);
				/**/
				echo json_encode($row);
				if($i++==$c)break;
				echo ',';
			};
		};
		if(isset($options['limit']) && $i>$options['limit']){
			echo ','.json_encode(Array('id'=>0,'name'=>translate('results_truncated_tighten_conditions')));
		};
		echo ']';
	}
	
	protected static function needQuote($type){
		switch ($type){
			case 'text':return 1;
			case 'longtext':return 1;
			case 'varchar':return 1;
			case 'enum':return 1;
			case 'date':return 1;
			case 'datetime':return 1;
			case 'timestamp':return 1;
			case 'time':return 1;			
			default:return 0;
		}		
	}
	
	protected function describe($table){
		$r=$this->query("DESCRIBE `".$table."`;");
		if(!$r->num_rows){
			throw new Exception('table doesnt exist');
			return Array(); 			
		}
		$a=Array();		
		while($u=$r->fetch_assoc()){
			$a[$u['Field']]=Array();
			$type=explode('(',$u['Type']);
			if(isset($type[1]))$type[1]=substr($type[1],0,-1);
			if($type[0]=='enum' && isset($type[1])){
				$a[$u['Field']]['enum']=Array();
				$a[$u['Field']]['type']='enum';
				$enums=explode(',',$type[1]);
				foreach($enums as $en)$a[$u['Field']]['enum'][]=str_replace("'","",$en);
			}else{					
				$a[$u['Field']]['type']=$type[0];
				if($a[$u['Field']]['type']=='bigint')$a[$u['Field']]['type']='int';else
				if($a[$u['Field']]['type']=='varbinary')$a[$u['Field']]['type']='text';
				
				if(isset($type[1]))	$a[$u['Field']]['size']=(int)$type[1];
				
				if(isset($a[$u['Field']]['size']) && @$a[$u['Field']]['size']==1 && $a[$u['Field']]['type']=='tinyint')$a[$u['Field']]['type']='boolean';
				if($a[$u['Field']]['type']=='boolean')$a[$u['Field']]['default']=0;
			};
			if(isset($u['Default']) && $u['Default']){
					$a[$u['Field']]['default']=$u['Default'];
			};
			$a[$u['Field']]['nq']=self::needQuote($type[0]);		
			if($u['Key']=='PRI')$a[$u['Field']]['key']=1;
			else
			if($u['Key']=='UNI')$a[$u['Field']]['unique']=1;
			//print_var($u);
			if($u['Null']=='YES')$a[$u['Field']]['NULL']=1;
			$end=substr($u['Field'],-3);	
			if($end=='_at' || $end=='_by'){
				$a[$u['Field']]['hidden']=1;
				$a[$u['Field']]['processonsave']=1;
			}					
		};
		if(isset($a['id']))$a['id']['hidden']=1;
		return $a;	
	}
	
	public function lastError(){
		return mysqli_error();
	}
	
	public function createTable($tablename){
		
	}	
	
		
	public function buildTable($tablename){
		$tablename=strtolower($tablename);
		$struct=$this->describe($tablename);
		foreach($struct as $k=>$v){	
				$struct[$k]['query']='`'.$tablename.'`.`'.$k.'`';
				if(isset($struct[$k]['key']))$struct[$k]['readonly']=1;
				if(substr($k,-3,3)=='_id')$struct[$k]['hidden']=1;	
		}
		$tablename[0]=strtoupper($tablename[0]);
		$definition="<?php\n\nclass Base".$tablename."Table extends BaseTable{\n";
		//$definition.="\tstatic \$database='".$this->database."';\n";
		$definition.="\tstatic \$fields=";
		$fields=var_export($struct,1).";";
		$fields=str_replace("\n","\n\t",$fields);	
		$definition.=$fields;	
		$definition.="\n\tstatic function getFields(){return self::\$fields;}\n";		
		//$definition.="\tpublic function getFields(){return self::\$fields;}\n";		
		$definition.="};\n\n";
		
		$definition.="\n\nclass Base".$tablename."Record extends BaseRecord{\n";
		$definition.="\tpublic \$fields=Array();\n\tpublic function __construct(\$p,\$f){\n";
		$definition.="\t\tBaseRecord::__construct(\$p,\$f);\n\t}";
		$definition.="\n};";
		$outfn='models/base/'.strtolower($tablename).'.php';
		$outfn=strtolower($outfn);
		if(!is_dir(dirname(PATH_CONFIG.$outfn)))mkdir(dirname(PATH_CONFIG.$outfn),0777,1);
		if(file_exists(PATH_CONFIG.$outfn))unlink(PATH_CONFIG.$outfn);
		file_put_contents(strtolower(PATH_CONFIG.$outfn),$definition);
		chmod(strtolower(PATH_CONFIG.$outfn),0766);
		
		$outfn2=PATH_CONFIG.'models/model.'.strtolower($tablename).'.php';
		$definition="<?php\nrequire_once PATH_CONFIG.'$outfn';\n\n";
		$definition.="class ".$tablename."Table extends Base".$tablename."Table{\n";
		$definition.="\tpublic function __construct(){\n\t\tBase".$tablename."Table::__construct();\n\t}\n};\n\n";
		$definition.="class ".$tablename."Record extends Base".$tablename."Record{\n";
		$definition.="\tpublic function __construct(\$p=0){\n\t\tparent::__construct(\$p,".$tablename."Table::\$fields);\n\t}\n};\n\n";
		foreach($struct as $k=>$v){
			if(substr($k,-3)!="_id")continue;
			$definition.="\n/*\n".$tablename."Table::\$fields['".substr($k,0,-3)."']=Array(\n\t\"type\"=>\"varchar\",\n".
				"\t\"query\"=>\"`".substr($k,0,-3)."`.`name`\",\n\t\"join\"=>Array(\n".			
				"\t\t\"table\"=>\"".substr($k,0,-3)."\",\n".			
				"\t\t\"column\"=>\"id\",\n".			
				"\t\t\"local\"=>\"$k\",\n".
				"\t)\n);\n/**/\n\n";			
		};
		if(!file_exists($outfn2)){
			file_put_contents($outfn2,$definition);
			chmod(strtolower(PATH_CONFIG.$outfn),0766);
		}
		return 0;	
	}
	
	public function query($q){
		$this->queries[]=$q;		
		$mt=microtime(1);
		$r=$this->engine->query($q);
		if(isset($_GET['DEBUGSQL']))console_log("$q ###".Round(microtime(1)-$mt,4).'sec');
		if(!$r){
			if(DEBUG && 0){
				obflush();
				echo "<pre>$q\n\n<b><i>".mysqli_error($this->engine)."</i></b>\n\n";
				debug_print_backtrace();
				echo '</pre>';
				exit;
			}else{
				throw new MySQLException($this->engine);			
			};
		};
		return $r;
	}
	
	public function queryAndFetchAll($q){
		$rs=$this->query($q);
		if(!$rs)return NULL;
		$r=Array();
		while($r[]=$rs->fetch_assoc());
		mysqli_free_result($rs);
		return $r;
	}
	
	public function __toString(){
		return $this->database;
	}
}
